package edu.hda.pt.utils;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map;

import edu.hda.pt.client.Config;
import edu.hda.pt.client.PTClient;

/**
 * Logger class for logging results. The results can either be printed on the
 * screen or saved to a file.
 * 
 * @author Marc Hammerton
 * 
 */
public class Logger {

	public static boolean logg2File = true;

	public static HashMap<String, long[]> testResults = new HashMap<String, long[]>();

	private static Writer fw = null;

	/**
	 * If the results are to be saved to a file, this method creates a new file.
	 * It takes the arguments and the date to create a file name.
	 * 
	 * @param server
	 *            - to determine if server is running on the localhost or remote
	 * @param connectionMethod
	 *            - the connection method currently used
	 * @param nrOfClients
	 *            - the number of clients which were simultaneously connected to
	 *            the server
	 * @param header
	 *            - the header which should be added in the first row of the
	 *            file
	 */
	public static void createFile(String server,
			ConnectionMethod connectionMethod, int nrOfClients, String header) {

		if (logg2File) {
			if (server.equalsIgnoreCase("127.0.0.1")
					|| server.equalsIgnoreCase("localhost")) {
				server = "loopback";
			} else {
				server = "ethernet";
			}

			// Current date
			SimpleDateFormat sdf = new SimpleDateFormat("yyMMdd_HHmmss");
			String date = sdf.format(Calendar.getInstance().getTime());

			String dirName = "logs/" + connectionMethod;
			String fileName = "";
			if (PTClient.test2run == TestMethod.DTP) {
				fileName = "/" + server + "_" + PTClient.test2run + "_"
						+ PTClient.datasize + "_" + connectionMethod + "_"
						+ date + "_" + Integer.toString(nrOfClients) + ".txt";
			} else if (PTClient.test2run == TestMethod.STREAMING) {
				fileName = "/" + server + "_" + PTClient.test2run + "_"
						+ PTClient.stream_fs + "_" + Config.STREAM_TIMESPAN
						+ "_" + connectionMethod + "_" + date + "_"
						+ Integer.toString(nrOfClients) + ".txt";
			} else {
				fileName = "/" + server + "_" + PTClient.test2run + "_"
						+ PTClient.datatype + "_" + connectionMethod + "_"
						+ date + "_" + Integer.toString(nrOfClients) + ".txt";
			}
			try {
				// Check if directory does not exists -> create it
				File dir = new File(dirName);
				if (!dir.exists() || !dir.isDirectory()) {
					if (!dir.mkdir()) {
						System.out.println("Error creating directory");
					}
				}

				fw = new FileWriter(dirName + fileName);
				fw.write(header);

			} catch (IOException e) {
				System.err.println("Couldn't create file");
			}
		}

	}

	/**
	 * Main method for logging the test results. If logg2file is true, the
	 * results are saved in a file.
	 * 
	 * @param id
	 *            - the client id
	 * @param results
	 *            - the results of the test
	 * @param server
	 *            - server location (either localhost or ethernet)
	 * @param connectionMethod
	 *            - the currently used connection method
	 * @param nrOfClients
	 *            - the number of clients which were simultaneously connected to
	 *            the server
	 */
	public static void logTestResults(String id, long[] results, String server,
			ConnectionMethod connectionMethod, int nrOfClients) {

		testResults.put(id, results);

		if (logg2File) {
			System.out.println("Saving results for " + id);

			String fieldSeparator = ";";
			String lineSeparator = System.getProperty("line.separator");

			// Create File if it does not exist already
			if (fw == null) {
				// Header
				String header = "ID" + fieldSeparator;
				if (PTClient.test2run.equals(TestMethod.STREAMING)) {
					header += PTClient.stream_fs + fieldSeparator;
				} else {
					header += PTClient.datatype + fieldSeparator;
				}
				header += lineSeparator;

				createFile(server, connectionMethod, nrOfClients, header);
			}

			if (fw != null) {
				try {
					for (int i = 0; i < results.length; i++) {
						fw.append(id + fieldSeparator + results[i]
								+ fieldSeparator + lineSeparator);
					}

				} catch (IOException e) {
					System.err.println("Couldn't create file");
				}
			}
		}
	}

	/**
	 * After the results are saved in the file, the file is closed
	 */
	public static void closeFile() {
		if (fw != null) {
			try {
				fw.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		fw = null;
	}

	/**
	 * Debugging method for logging several test times
	 * 
	 * @param id - the client id
	 * @param mark - the marker to distinguish the test times
	 * @param time - the measured time
	 */
	public static void logTestTimes(String id, int mark, long time) {
		long[] times;
		if (!testResults.containsKey(id)) {
			times = new long[10];
			testResults.put(id, times);
		} else {
			times = testResults.get(id);
		}

		times[mark] += time;
		testResults.put(id, times);
	}

	/**
	 * Debugging method for printing the test times
	 */
	public static void printTestTimes() {
		long[] finalTimes = new long[10];
		for (Map.Entry<String, long[]> entry : testResults.entrySet()) {
			String id = entry.getKey();
			long[] times = entry.getValue();

			long prevTime = 0L;
			if (id.contains("client")) {
				prevTime = times[0] / Config.METHOD_CALLS;
			} else {
				prevTime = times[0]
						/ (Config.METHOD_CALLS * Config.NR_CLIENTS[0]);
			}

			for (int i = 0; i < times.length; i++) {
				if (times[i] == 0) {
					finalTimes[i] = -1;
					continue;
				}

				if (id.contains("client")) {
					times[i] = times[i] / Config.METHOD_CALLS;
				} else {
					times[i] = times[i]
							/ (Config.METHOD_CALLS * Config.NR_CLIENTS[0]);
				}
				finalTimes[i] += (times[i] - prevTime);
				prevTime = times[i];
			}
		}

		if (testResults.size() > 0) {

			System.out
					.println("***********************************************");
			System.out.println("Test Results");
			System.out.println("------------------------");
			long totalTime = 0;
			for (int i = 0; i < finalTimes.length; i++) {
				if (finalTimes[i] == -1) {
					continue;
				}

				finalTimes[i] = finalTimes[i] / testResults.size();
				totalTime += finalTimes[i];
				System.out.println(i + ":\t" + (finalTimes[i]));
			}
			System.out.println("Total time: " + (totalTime));
		}
	}

	/**
	 * Print the test results of the round trip time test
	 */
	public static void printTestRTT() {
		double finalTime = 0;
		for (Map.Entry<String, long[]> entry : testResults.entrySet()) {
			long[] times = entry.getValue();

			long rttAvg = 0;
			for (int i = 0; i < Config.METHOD_CALLS; i++) {
				rttAvg += times[i];
			}
			finalTime += (rttAvg / Config.METHOD_CALLS);
		}

		if (testResults.size() > 0) {

			System.out
					.println("***********************************************");
			System.out.println("Test Result");
			System.out.println("------------------------");
			long avg = (long) (finalTime / testResults.size());
			System.out.println("Average time: " + avg);
		}
	}

	/**
	 * Print the test results of the streaming test
	 */
	public static void printStreamResults() {
		for (Map.Entry<String, long[]> entry : testResults.entrySet()) {
			String id = entry.getKey();
			long[] count = entry.getValue();

			System.out.print("Results for " + id);
			System.out.print("\tFrames:\t" + count[0]);
			System.out.print("\tFPS:\t"
					+ (count[0] / (Config.STREAM_TIMESPAN / 1000)) + "\n");
		}
	}

	/**
	 * Print the test results of the throughput test
	 */
	public static void printThroughput() {
		long totalCount = 0;
		for (Map.Entry<String, long[]> entry : testResults.entrySet()) {
			totalCount += entry.getValue()[0];
		}

		System.out.println("Average Throughput: "
				+ (totalCount / testResults.size()));
	}

}
